package com.thiswhyme.redis.lock;

import com.thiswhyme.redis.lock.spel.ExpressionUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁注解切面
 *
 * @author thiswhyme
 * @date 2022/10/25
 */
@Aspect
@Slf4j
@AllArgsConstructor
public class DistributedLockAspect {

    private final RedissonClient redissonClient;

    @Around("@annotation(DistributedLock)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        DistributedLock lockAnno = method.getAnnotation(DistributedLock.class);
        if (null == lockAnno) {
            return pjp.proceed();
        }
        String lockKey = genLockKey(pjp, lockAnno);
        if (!StringUtils.hasText(lockKey)) {
            return pjp.proceed();
        }
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(lockAnno.waitTime(), lockAnno.leaseTime(), TimeUnit.SECONDS)) {
                return pjp.proceed();
            } else {
                // TODO
                log.warn("加锁失败，请稍后再试");
                return null;
            }
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 组装lockKey
     *
     * @param pjp
     * @param lockAnno
     * @return
     */
    private String genLockKey(ProceedingJoinPoint pjp, DistributedLock lockAnno) {
        String keyPrefix = lockAnno.keyPrefix();
        String[] keyExpressions = lockAnno.key();
        StringBuilder lockKeyBuffer = new StringBuilder();
        if (StringUtils.hasText(keyPrefix)) {
            lockKeyBuffer.append(keyPrefix).append(":");
        }
        if (null != keyExpressions && keyExpressions.length > 0) {
            for (int i = 0; i < keyExpressions.length; i++) {
                lockKeyBuffer.append(ExpressionUtils.genKey(pjp, keyExpressions[i]));
                if (i < keyExpressions.length - 1) {
                    lockKeyBuffer.append("-");
                }
            }
        }
        String lockKey = lockKeyBuffer.toString();
        log.debug("lock key is {}", lockKey);
        return lockKey;
    }
}
